package pers.course.week11.CreateYourOwnException;

import java.util.Scanner;
import java.text.NumberFormat;

class OverdraftException extends Exception {
    private double deficit;

    public OverdraftException(String message, double deficit) {
        super(message);
        this.deficit = deficit;
    }

    public double getDeficit() {
        return deficit;
    }
}

class Account {
    protected double balance;

    Account(double init_balance) {
        balance = init_balance;
    }

    double getBalance() {
        return balance;
    }

    boolean deposit(double amt) {
        balance += amt;
        return true;
    }

    void withdraw(double amt) throws OverdraftException {
        if (amt <= balance) {
            balance -= amt;
        } else throw new OverdraftException("Insufficient funds", amt - balance);
    }
}

class SavingsAccount extends Account {
    double interestRate;
    static double itsbalance;

    SavingsAccount(double balance, double interest_rate) {
        super(balance);
        interestRate = interest_rate;
    }
}

class CheckingAccount extends Account {
    double overdraftProtection = 0;

    CheckingAccount(double balance) {
        super(balance);
    }

    CheckingAccount(double balance, double protect) {
        super(balance);
        overdraftProtection = protect;
    }

    void withdraw(double amt) throws OverdraftException {
        if (amt <= balance) balance -= amt;
        else {
            if (overdraftProtection == 0) throw new OverdraftException("No overdraft protection", amt - balance);
            else {
                if (amt - balance <= overdraftProtection) {
                    overdraftProtection -= (amt - balance);
                    balance = 0;
                } else throw new OverdraftException("Insufficient funds", amt - balance - overdraftProtection);
                ;
            }
        }
    }
}

class Customer {
    String firstName;
    String lastName;
    Account[] account = new Account[5];
    int numOfAccount;

    Customer(String f, String l) {
        firstName = f.substring(0);
        lastName = l.substring(0);
        numOfAccount = 0;
    }

    void addAccount(Account acc) {
        account[numOfAccount] = acc;
        numOfAccount++;
    }

    Account getAccount(int index) {
        return account[index];
    }

    String getFirstName() {
        return firstName;
    }

    String getLastName() {
        return lastName;
    }

    int getNumOfAccounts() {
        return numOfAccount;
    }

    public String toString() {
        return "[" + firstName + " " + lastName + "]";
    }
}

class Bank {
    Customer[] customer;
    int numberOfCustomers;
    static Bank it = null;

    private Bank() {
        customer = new Customer[20];
        numberOfCustomers = 0;
    }

    void addCustomer(String f, String l) {
        Customer person = new Customer(f, l);
        customer[numberOfCustomers] = person;
        numberOfCustomers++;
    }

    int getNumOfCustomers() {
        return numberOfCustomers;
    }

    Customer getCustomer(int index) {
        return customer[index];
    }

    static Bank getBank() {
        if (it == null) {
            it = new Bank();
            return it;
        } else return it;
    }
}

class CustomerReport {
    public void generateReport() {
        NumberFormat currency_format = NumberFormat.getCurrencyInstance();
        Bank bank = Bank.it;/*** STEP 1: RETRIEVE BANK SINGLETON OBJECT HERE ***/
        Customer customer;
        System.out.println("CUSTOMERS REPORT");
        System.out.println("================");
        for (int cust_idx = 0; cust_idx < bank.getNumOfCustomers(); cust_idx++) {
            customer = bank.getCustomer(cust_idx);
            System.out.println();
            System.out.println("Customer: ["
                    + customer.getFirstName() + " "
                    + customer.getLastName() + "]");
            for (int acct_idx = 0; acct_idx < customer.getNumOfAccounts(); acct_idx++) {
                Account account = customer.getAccount(acct_idx);
                String account_type = "";
                // Determine the account type
                if (account instanceof SavingsAccount) {
                    account_type = "Savings Account";
                } else if (account instanceof CheckingAccount) {
                    account_type = "Checking Account";
                } else {
                    account_type = "Unknown Account Type";
                }
                // Print the current balance of the account
                String last = new String(currency_format.format(account.getBalance()).substring(0, currency_format.format(account.getBalance()).length() - 1));
                last = last.replaceAll(",", "");
                System.out.println("    " + account_type + ": current balance is "
                        + last);
            }
        }
    }
}
/* PRESET CODE BEGIN - NEVER TOUCH CODE BELOW */
public class CreateYourOwnException {

    public static void main(String[] args) {
        Bank bank = Bank.getBank();

        Customer customer;
        int curCustomer = 0;
        Scanner s = new Scanner(System.in);
        int t = s.nextInt();
        s.nextLine();
// Create several customers and their accounts according to data
        while (t > 0) {
            String f = s.next();
            String l = s.next();
            s.nextLine();
            bank.addCustomer(f, l);
            customer = bank.getCustomer(curCustomer++);
            int numAccount = s.nextInt();
            s.nextLine();
            while (numAccount-- > 0) {
                String[] type = s.nextLine().split(" ");
                double balance;
                double interesOrProtect;
                if (type[0].trim().toUpperCase().equals("C")) {
                    balance = Double.parseDouble(type[1]);
                    if (type.length == 3) {
                        interesOrProtect = Double.parseDouble(type[2]);
                        customer.addAccount(new CheckingAccount(balance,
                                interesOrProtect));
                    } else {
                        customer.addAccount(new CheckingAccount(balance));
                    }
                } else if (type[0].trim().toUpperCase().equals("S")) {
                    balance = Double.parseDouble(type[1]);
                    interesOrProtect = Double.parseDouble(type[2]);
                    customer.addAccount(new SavingsAccount(balance,
                            interesOrProtect));
                }
            }
            t--;
        }

        int nOPs = s.nextInt();
        s.nextLine();
        while (nOPs-- > 0) {
            String[] sOP = s.nextLine().split(" ");
            char op = sOP[0].charAt(0);
            int customerIndex = Integer.parseInt(sOP[1]);
            int accountIndex = Integer.parseInt(sOP[2]);
            double amount = Double.parseDouble(sOP[3]);
            switch (op) {
                case 'w':
                case 'W':
                    customer = bank.getCustomer(customerIndex);
                    try {
                        customer.getAccount(accountIndex).withdraw(amount);
                    } catch (OverdraftException ode) {
                        System.out.println(customer + " withdraw " + amount + ", " + ode.getMessage() + ": " + ode.getDeficit());
                    }
                    break;
                case 'd':
                case 'D':
                    customer = bank.getCustomer(customerIndex);
                    customer.getAccount(accountIndex).deposit(amount);
                    break;
            }
        }

// Generate a report
        CustomerReport cr = new CustomerReport();
        cr.generateReport();
    }
}
/* PRESET CODE END - NEVER TOUCH CODE ABOVE */